package own.stu.jobgib.playown.vediodown.common.service;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Consumer;

public class QueueService<D extends Runnable, Q extends Runnable> {

    protected static Logger logger = LoggerFactory.getLogger(QueueService.class);

    private int query_task_queue_size = 100;
    private int load_task_queue_size = 100;
    private LinkedBlockingDeque<Q> queryTaskQueue = new LinkedBlockingDeque(query_task_queue_size);
    private ThreadPoolExecutor queryThreadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    private LinkedBlockingDeque<D> loadTaskQueue = new LinkedBlockingDeque(load_task_queue_size);
    private ThreadPoolExecutor loadThreadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

    public void start() {
        loadThreadPool.submit(() -> {
            D task = null;
            while ((task = getTask()) != null) {
                loadThreadPool.submit(task);
            }
        });

        queryThreadPool.submit(() -> {
            Q task = null;
            while ((task = getQuery()) != null) {
                queryThreadPool.submit(task);
            }
        });
    }

    private <M> void add(LinkedBlockingDeque<M> queue, M data) {
        try {
            queue.put(data);
        } catch (InterruptedException e) {
            logger.error("add {} ele to queue {} error", data, loadTaskQueue);
        }
    }

    public void addTask(D data) {
        add(loadTaskQueue, data);
    }

    public void addQueue(Q data) {
        add(queryTaskQueue, data);
    }

    private <M> M get(LinkedBlockingDeque<M> queue) {
        try {
            return queue.take();
        } catch (InterruptedException e) {
            logger.error("get from queue {} error", loadTaskQueue);
        }
        return null;
    }

    public D getTask() {
        return get(loadTaskQueue);
    }

    public Q getQuery() {
        return get(queryTaskQueue);
    }

    public ThreadPoolExecutor getQueryThreadPool() {
        return queryThreadPool;
    }

    public ThreadPoolExecutor getLoadThreadPool() {
        return loadThreadPool;
    }

    @Data
    @AllArgsConstructor
    public static class BaseTask<T> implements Runnable {
        private List<T> itemList;
        Consumer<T> consumer;

        @Override
        public void run() {
            for (T simpleMediaItem : itemList) {
                consumer.accept(simpleMediaItem);
            }
        }
    }

    public boolean hasTasks() {
        return !queryTaskQueue.isEmpty() && !loadTaskQueue.isEmpty()
                && !loadThreadPool.getQueue().isEmpty() && !queryThreadPool.getQueue().isEmpty();
    }

    public void destory() {
        this.loadThreadPool.shutdown();
        this.queryThreadPool.shutdown();
    }
}
